package exilim.exf1.cspacker.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.Tag;

/**
 * 
 * @author Janos
 * @version 04.10.2009 | 20:38:36
 * 
 */
public class HtmlInfo {

	static final class Pair<A, B> {
		final A a;
		final B b;

		public Pair(final A a, final B b) {
			this.a = a;
			this.b = b;
		}

		@Override
		public boolean equals(final Object b) {
			if (b instanceof Pair<?, ?>) {
				return this.a.equals(((Pair<?, ?>) b).a);
			}
			return this.a.equals(b);
		}

		@Override
		public int hashCode() {
			return this.a.hashCode();
		}
	}

	static final String PICTURES_FOLDER_NAME = "pictures2"; //$NON-NLS-1$

	/**
	 * 0x3101 --> Serie identifier 0x3102 --> Serie numbering
	 * 
	 * 
	 * @param args
	 */
	public static void main(final String[] args) {

		final File dir = new File(PICTURES_FOLDER_NAME);
		if (!dir.exists()) {
			System.err.println("Directory doesn't exist."); //$NON-NLS-1$
			return;
		}

		final File[] jpgFiles = dir.listFiles(new FileFilter() {
			@Override
			public boolean accept(final File pathname) {
				return pathname.getName().toLowerCase().endsWith(".jpg"); //$NON-NLS-1$
			}
		});

		/**
		 * Map<[DirectoryInfo], [TagsMap]>> [DirectoryInfo] : Pair<[DirectoryClass],
		 * [DirectoryName]> [DirectoryClass] : Class<Directory> (Identifying class of Directory)
		 * [DirectoryName] : String (Name of Directory) [TagsMap] : Map<[TagInfo], [FileMap]>>
		 * [TagInfo] : Pair<[TagType], [TagName]> [TagType] : Integer (Identifying a Tag) [TagName]
		 * : String (Name of the Tag) [FileMap] : Map<[Filename], [TagValue]> [Filename] : String
		 * (Name of the file) [TagValue] : Pair<[RealValue], [StringValue]> [RealValue] : Object
		 * (Value of the Tag) [StringValue] : String (Interpretation of the Tag Value, if available)
		 */

		final Map<Pair<Class<?>, String>, Map<Pair<Integer, String>, Map<String, Pair<Object, String>>>> MAP = new Hashtable<Pair<Class<?>, String>, Map<Pair<Integer, String>, Map<String, Pair<Object, String>>>>();

		for (final File file : jpgFiles) {
			final Metadata metadata;
			try {
				metadata = JpegMetadataReader.readMetadata(file);
			} catch (final JpegProcessingException e) {
				e.printStackTrace();
				continue;
			}

			for (final Iterator<?> directoryIterator = metadata.getDirectoryIterator(); directoryIterator.hasNext();) {
				final Directory directory = (Directory) directoryIterator.next();

				Map<Pair<Integer, String>, Map<String, Pair<Object, String>>> tagsMap = MAP.get(directory.getClass());
				if (tagsMap == null) {
					tagsMap = new TreeMap<Pair<Integer, String>, Map<String, Pair<Object, String>>>(comparator);
					final Pair<Class<?>, String> directoryInfo = new Pair<Class<?>, String>(directory.getClass(), directory.getName());
					MAP.put(directoryInfo, tagsMap);
				}

				for (final Iterator<?> tagIterator = directory.getTagIterator(); tagIterator.hasNext();) {

					final Tag tag = (Tag) tagIterator.next();

					final Pair<Integer, String> tagInfo = new Pair<Integer, String>(tag.getTagType(), tag.getTagName());
					Map<String, Pair<Object, String>> fileMap = tagsMap.get(tagInfo);
					if (fileMap == null) {
						fileMap = new Hashtable<String, Pair<Object, String>>();
						tagsMap.put(tagInfo, fileMap);
					}

					String stringValue = null;
					try {
						stringValue = tag.getDescription();
					} catch (final MetadataException e) {
					}
					final Object realValue = directory.getObject(tag.getTagType());
					fileMap.put(file.getName(), new Pair<Object, String>(realValue, stringValue));

				}
			}
		}

		final StringBuilder HTML = new StringBuilder();
		HTML.append("<?xml version='1.0' encoding='UTF-8' ?>\n");
		HTML
				.append("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n");
		HTML.append("<html xmlns='http://www.w3.org/1999/xhtml'>\n");
		HTML.append("<head>\n");
		HTML.append("\t<title>EXIF</title>\n");
		HTML.append("\t<link rel='stylesheet' type='text/css' href='style.css' />\n");
		HTML.append("</head>\n");
		HTML.append("<body>\n");
		HTML.append(createTable(jpgFiles, MAP));
		HTML.append("</body>\n");
		HTML.append("</html>\n");

		final File out = new File("exif/exif.html");
		try {
			out.createNewFile();
			final BufferedWriter writer = new BufferedWriter(new FileWriter(out));
			writer.write(HTML.toString());
			writer.close();
		} catch (final IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * @param map
	 * @return
	 */
	private static StringBuilder createTable(final File[] files,
			final Map<Pair<Class<?>, String>, Map<Pair<Integer, String>, Map<String, Pair<Object, String>>>> map) {
		final StringBuilder TABLE = new StringBuilder();
		TABLE.append("\t<table border='1'>\n");
		// TABLE.append("\t\t<colgroup>\n");
		// TABLE.append("\t\t\t<col width='50%' />\n");
		// TABLE.append("\t\t\t<col width='100' span='" + files.length + "' />\n");
		// TABLE.append("\t\t</colgroup>\n");

		TABLE.append("\t\t<tr class='table_title'>\n");

		TABLE.append("\t\t\t<th>TAG</th>\n");

		for (final File jpg : files) {
			TABLE.append("\t\t\t<th>");
			TABLE.append(jpg.getName());
			TABLE.append("\t\t\t</th>\n");
		}
		TABLE.append("\t\t</tr>\n");

		TABLE.append(createTableRows(files, map));

		TABLE.append("\t</table>\n");
		return TABLE;
	}

	/**
	 * @param files
	 * @param map
	 * @return
	 */
	private static StringBuilder createTableRows(final File[] files,
			final Map<Pair<Class<?>, String>, Map<Pair<Integer, String>, Map<String, Pair<Object, String>>>> map) {

		final StringBuilder ROWS = new StringBuilder();

		final Set<Entry<Pair<Class<?>, String>, Map<Pair<Integer, String>, Map<String, Pair<Object, String>>>>> entrySet = map.entrySet();
		for (final Entry<Pair<Class<?>, String>, Map<Pair<Integer, String>, Map<String, Pair<Object, String>>>> entry : entrySet) {

			ROWS.append("\t\t<tr class='table_directory'>\n");
			ROWS.append("\t\t\t<td colspan='" + (files.length + 1) + "'>");
			ROWS.append(entry.getKey().b).append(" (").append(entry.getKey().a).append(")");
			ROWS.append("</td>\n");
			ROWS.append("\t\t</tr>\n");

			ROWS.append(createDirectoryRows(files, entry.getValue()));
		}

		return ROWS;
	}

	static final int COLUMN_WIDTH = 20;

	private static Comparator<Pair<Integer, String>> comparator = new Comparator<Pair<Integer, String>>() {
		@Override
		public int compare(final Pair<Integer, String> o1, final Pair<Integer, String> o2) {
			return o1.b.compareTo(o2.b);
		}
	};

	/**
	 * @param files
	 * @param map
	 * @return
	 */
	private static StringBuilder createDirectoryRows(final File[] files,
			final Map<Pair<Integer, String>, Map<String, Pair<Object, String>>> map) {
		final StringBuilder ROWS = new StringBuilder();

		for (final Entry<Pair<Integer, String>, Map<String, Pair<Object, String>>> entry : map.entrySet()) {

			final Map<String, Pair<Object, String>> tagInfo = entry.getValue();

			ROWS.append("\t\t<tr>\n");
			ROWS.append("\t\t\t<td nowrap='nowrap' class='table_tag'>");
			ROWS.append(entry.getKey().b).append(" [").append(entry.getKey().a).append("]");
			ROWS.append("</td>\n");

			for (final File file : files) {

				final Pair<Object, String> tagsPair = tagInfo.get(file.getName());

				StringBuilder sb = new StringBuilder();
				if (tagsPair.b != null) {
					sb.append(tagsPair.b).append(" ");
				}
				// sb.append("(");
				// sb.append(tagValueToString(tagsPair.a));
				// sb.append(")");

				ROWS.append("\t\t\t<td nowrap='nowrap'");
				if (sb.length() > COLUMN_WIDTH + 4) {
					ROWS.append(" title='").append(sb).append("'");
					sb = sb.delete(COLUMN_WIDTH, sb.length());
					sb.append(" ...");
				}
				ROWS.append(">");
				ROWS.append(sb);
				ROWS.append("</td>\n");
			}

			ROWS.append("\t\t</tr>\n");

		}

		return ROWS;
	}

	/**
	 * @param object
	 * @return
	 */
	@SuppressWarnings("unused")
	private static String tagValueToString(final Object tagValue) {
		if (tagValue == null) {
			return "";
		}
		final Class<?> valueClass = tagValue.getClass();

		if (Number.class.isAssignableFrom(valueClass) || CharSequence.class.isAssignableFrom(valueClass)) {
			return tagValue.toString();
		}

		if (valueClass.isArray()) {
			return printArray(tagValue);
		}

		return "---";
	}

	private static String printArray(final Object anArray) {
		if (!anArray.getClass().isArray()) {
			System.err.println("error");
			return "-------------";
		}

		final StringBuilder sb = new StringBuilder();
		sb.append("[");
		final int length = Array.getLength(anArray);
		for (int i = 0; i < length - 1; i++) {
			sb.append(Array.get(anArray, i)).append(" ");
		}
		sb.append(Array.get(anArray, length - 1));
		sb.append("]");

		return sb.toString();
	}

}
